/*
	MIDAS Context Based Routing
	cbr_msgcache.cc
	Copyright (c) 2007 Radoslaw Olgierd Schoeneich
	MEAG IT PW
 */

#include <algorithm>

#include "scheduler.h"
#include "packet.h"

#include "cbr_msgcache.h"
#include "cbr_config.h"

/**
 * CBR message constructor.
 *
 * @param total total length of the data
 * @param length current length (length of processed data)
 * @param payload pointer to the buffer containing payload data
 * @param addr context based address of the destination
 * @param tid transaction ID
 * @param state current state of the message
 * @param expire message timeout
 */

cbr_Msg::cbr_Msg(int32_t total, int32_t length, const char* payload,
		 const cbr_ContextAddress* addr, u_int32_t tid,
		 state_t state, double expire)
{
	this->total = total;
	this->length = length;
	this->payload = new char[total];
	if (payload)
		std::copy(payload, payload + length, this->payload);
	else
		std::fill(this->payload, this->payload + total, 0x77);
	this->state = state;
	this->mcast = false;
	this->addr = new cbr_ContextAddress(*addr);
	this->transactionID = tid;
	this->expire = sclock() + expire;
}

/**
 * CBR message desctructor.
 */

cbr_Msg::~cbr_Msg()
{
	if (this->payload)
		delete[] this->payload;
	if (this->addr)
		delete this->addr;
}

/**
 * Get CBR message payload.
 *
 * @param dest pointer to the destination buffer
 * @param offset offest into the payload data
 * @param len maximum number of bytes to copy
 *
 * Copy data from the message payload to the destination buffer
 * pointed by @a dest given offset into the payload data @a offset
 * and the maximum number of bytes to copy @a len.
 *
 * @return number of bytes copied from message payload to the
 * destination buffer.
 */

int32_t cbr_Msg::getPayload(char* dest, int32_t offset, int32_t len)
{
	int32_t ret = 0;

	if (offset < this->total) {
		ret = this->total - offset;
		if (ret > len)
			ret = len;
		std::copy(this->payload + offset,
			  this->payload + offset + ret, dest);
	}

	if (offset == 0)
		this->length = ret;
	return ret;
}

/**
 * Add new data to the CBR message payload.
 *
 * @param data pointer to the source buffer
 * @param offset offest into the payload data
 * @param len number of bytes to copy
 *
 * @return current state of the CBR message.
 */

cbr_Msg::state_t cbr_Msg::add(const char* data, int32_t offset, int32_t len)
{
	std::copy(data, data + len, this->payload + offset);
	this->length = offset + len;
	if (this->length == this->total)
		this->state = COMPLETE;

	return this->state;
}

/**
 * CBR message cache constructor.
 */

cbr_MsgCache::cbr_MsgCache()
{
	sem_init(&msgcache_sem, 0, 1);
	count = 0;
}

/**
 * CBR message cache destructor.
 */

cbr_MsgCache::~cbr_MsgCache()
{
	sem_wait(&msgcache_sem);
	while (!queue.empty()) {
		cbr_Msg* m = queue.front();
		delete m;
		queue.pop_front();
	}
	sem_post(&msgcache_sem);

	sem_destroy(&msgcache_sem);
}

/**
 * Add new CBR message to the CBR message cache.
 *
 * @param msg CBR message
 */

void cbr_MsgCache::enqueue(cbr_Msg* msg)
{
	sem_wait(&msgcache_sem);
	queue.push_back(msg);
	count++;
	sem_post(&msgcache_sem);
}

/**
 * Lookup CBR message cache for a message given its transaction ID.
 *
 * @param tid transaction ID
 * @param dequeue flag to remove CBR message from cache
 *
 * @return CBR message if found in the CBR message cache, NULL otherwise. 
 */

cbr_Msg* cbr_MsgCache::lookup(u_int32_t tid, bool dequeue)
{
	msgs_list::iterator i;

	sem_wait(&msgcache_sem);
	for (i = queue.begin(); i != queue.end(); i++) {
		if ((*i)->getTransactionID() == tid) {
			cbr_Msg* tmp = *i;
			if (dequeue)
				queue.erase(i);
			sem_post(&msgcache_sem);
			return tmp;
		}
	}
	sem_post(&msgcache_sem);

	return NULL;
}

/**
 * Purge CBR message cache.
 */

void cbr_MsgCache::purge()
{
	msgs_list::iterator i, j;

	sem_wait(&msgcache_sem);
	if (count == 0) {
		sem_post(&msgcache_sem);
		return;
	}

	for (i = queue.begin(); i != queue.end(); ) {
		if ((*i)->expires() < sclock()) {
			j = i;
			i++;
			delete (*j);
			queue.erase(j);
		} else {
			i++;
		}
	}
	sem_post(&msgcache_sem);
}
